package org.com.yz.dubbo.service.impl;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.XmlUtil;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.mysql.jdbc.authentication.MysqlClearPasswordPlugin;
import org.apache.commons.collections.functors.ConstantFactory;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.entity.ContentType;
import org.apache.http.protocol.HTTP;
import org.com.yz.common.config.ConfigureTest;
import org.com.yz.common.config.IRequestService;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.com.yz.common.constant.PayConstant;
import org.com.yz.common.util.*;
import org.com.yz.dal.dao.mapper.*;
import org.com.yz.dal.dao.model.*;
import org.com.yz.dubbo.api.service.IHisAccntOnway2Service;
import org.com.yz.dubbo.api.service.IHisAccntOnwayService;
import org.com.yz.dubbo.api.service.IMchConfigService;
import org.com.yz.dubbo.api.service.ITransactionDetailsService;
import org.com.yz.dubbo.service.RedisService;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import sun.net.ftp.FtpClient;

import java.io.*;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * author wzc123
 * since 2018-07-31
 */
@Service(version = "1.0.0", interfaceName = "org.com.yz.dubbo.api.service.ITransactionDetailsService")
public class TransactionDetailsServiceImpl implements ITransactionDetailsService {


    @Autowired
    private TransactionDetailsMapper transactionDetailsMapper;
    @Autowired
    private MchMapper mchMapper;
    @Autowired
    private HisAccntOnwayMapper hisAccntOnwayMapper;
    @Autowired
    private HisAccntOnway2Mapper hisAccntOnway2Mapper;
    @Autowired
    private MchAccntMapper mchAccntMapper;
    @Autowired
    private DepositTransactionMapper depositTransactionMapper;
    @Autowired
    private MchAccntChargeUpMapper mchAccntChargeUpMapper;
    @Autowired
    private HisAccntPrepayMapper hisAccntPrepayMapper;
    @Autowired
    private HisAccntMchMapper hisAccntMchMapper;
    @Autowired
    private IHisAccntOnwayService iHisAccntOnwayService;
    @Autowired
    private IMchConfigService iMchConfigService;
    @Autowired
    private IHisAccntOnway2Service iHisAccntOnway2Service;
    @Autowired
    private AccountDetailsMapper accountDetailsMapper;
    @Autowired
    private ReconciliationResultInfoMapper resultInfoMapper;
    @Autowired
    private ReconciliationResultMapper resultMapper;

    @Autowired
    private ProblemRecordMapper problemRecordMapper;


    @Autowired
    private RedisService redisService;


    @Autowired
    private DownloadInfoMapper downloadInfoMapper;

    @Autowired
    private ReserveRechargeMapper reserveRechargeMapper;


    private static Logger log = LoggerFactory.getLogger(TransactionDetailsServiceImpl.class);


    //    private int page = 1;
    private Double amout = 0d;

    private List<TransactionDetails> transactionDetailslist = new ArrayList<>();


    /**
     * 发送报文请求
     * <p>
     * return
     */
    @Override
    public Map<String, Object> doPost(String beginDate, String endDate, int page, int type, MchConfig mchConfigSupser
            , String remark) {
        //String beginDate, String endDate, int page, int type,MchConfig config
        Map<String, Object> resMap = new HashMap<>();
        Double res = 0d;
        BufferedReader br = null;
        OutputStreamWriter out = null;
        try {
            URL url = new URL(mchConfigSupser.getPay_url());
            URLConnection con = url.openConnection();
            con.setDoOutput(true);
            con.setRequestProperty("Cache-Control", "no-cache");
            con.setRequestProperty("Content-Type", "text/xml");

            out = new OutputStreamWriter(con.getOutputStream(), "gbk");
            //组装查询交易记录流水所需xml
            String xmlInfo = getXmlInfo(beginDate, endDate, page, mchConfigSupser, type);
            log.info(mchConfigSupser.getMchNo() + remark + "账户交易明细查询报文" + XmlFormatter.formatXml(xmlInfo));
            out.write(new String(xmlInfo.getBytes("gbk"), "gbk"));
            out.flush();
            out.close();

            br = new BufferedReader(new InputStreamReader(con.getInputStream(), "gbk"));
            String results = "";
            int tmp;
            while ((tmp = br.read()) != -1) {
                results += (char) tmp;
            }
            //解析查询交易记录流水返回报文
            resMap = requestXmlToObject(results, mchConfigSupser);

            if (ToolUtil.isNotEmpty(resMap.get("errMsg"))) {
                return resMap;
            }
            //如果还有下一页 则继续发送请求
            if ("Y".equals(resMap.get("more"))) {
                page += 1;
                doPost(beginDate, endDate, page, type, mchConfigSupser, remark);
            }
            transactionDetailslist = new ArrayList<>();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
            log.info("银行接口调用失败" + mchConfigSupser.getMchNo() + "####" + mchConfigSupser.getPay_url() + "####" + e.getMessage());
            resMap.put("errMsg", ToolUtil.isNotEmpty(e.getMessage()) ? e.getMessage() : "银行接口调用失败");
            return resMap;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        if (ToolUtil.isNotEmpty(resMap)) {
            resMap.put("amoutCount", res);
            resMap.put("page", page);
        }
        return resMap;
    }


    /**
     * 拼接报文
     * <p>
     * return
     */
    private String getXmlInfo(String beginDate, String endDate, int page, MchConfig mchConfigSupser, int type) {
        //String beginDate,String endDate, int page,MchConfig yjConfig,int type
        StringBuilder sb = new StringBuilder();
        sb.append("<?xml version=\"1.0\" encoding=\"GBK\"?>");
        sb.append("    <FOX>");
        sb.append("    <SIGNONMSGSRQV1>");
        sb.append("        <SONRQ>");
        sb.append("           <DTCLIENT>" + cn.hutool.core.date.DateUtil.now() + "</DTCLIENT>");            //客户端日期时间
        sb.append("           <CID>" + mchConfigSupser.getCid() + "</CID>");                                                   //企业网银客户号，10位数字字符
        sb.append("           <USERID>" + mchConfigSupser.getUserid() + "</USERID>");                                         // 登录用户名，最长：20位
        sb.append("           <USERPASS>" + AES.getInstance().decrypt(mchConfigSupser.getUserpass()) + "</USERPASS>");         //密码采用aes加密                      //登录密码，最长：30位
        sb.append("           <GENUSERKEY>N</GENUSERKEY>");
        //是否需要服务器产生USERKEY,，填Y、N
        sb.append("         </SONRQ>");

        sb.append("</SIGNONMSGSRQV1>");
        sb.append("  <SECURITIES_MSGSRQV1>");
        sb.append("   <SCUSTSTMTTRNRQ>");
        sb.append("     <TRNUID>" + DateUtil.getSeqString() + RandomNum.createRandomString(4) + "</TRNUID>");
        //客户端交易的唯一标志  YYYYMMDD+序号
        sb.append("   <SCUSTSTMTRQ>");
        sb.append("      <ACCTFROM>");
        sb.append("        <ACCTID>" + mchConfigSupser.getAcctid() + "</ACCTID>");           //付款人账户
        sb.append("      </ACCTFROM>");
        sb.append("       <INCTRAN>");
        sb.append("          <DTSTART>" + beginDate + "</DTSTART>");     //开始时间 格式：YYYY-MM-DD
        sb.append("          <DTEND>" + endDate + "</DTEND>");         //结束时间 格式：YYYY-MM-DD
        sb.append("          <PAGE>" + page + "</PAGE>");                    //请求响应的页数（代表从第几页开始查询）（必输)
        sb.append("           <TRNTYPE>" + type + "</TRNTYPE>");            //交易类型：0表示借方(往帐)  1表示贷方(来帐)默认查询借贷双方全部流水
        sb.append("       </INCTRAN>");
        sb.append("  </SCUSTSTMTRQ>");
        sb.append("</SCUSTSTMTTRNRQ>");
        sb.append("</SECURITIES_MSGSRQV1>");
        sb.append("</FOX>");
        return sb.toString();
    }

    /**
     * 解析返回的报文
     * <p>
     * param responseXml
     * return
     * throws Exception
     */
    @Override
    public Map<String, Object> requestXmlToObject(String responseXml, MchConfig mchConfigSupser)  {
//        log.info("交易记录返回报文" + responseXml);
        Map<String, Object> map = new HashMap<>();
        Document document = null;
        try {
            document = DocumentHelper.parseText(responseXml);
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        Element root = document.getRootElement();  // 获得根节点；

        Element ele = root.element("SIGNONMSGSRSV1");
        Element ele1 = ele.element("SONRS");
        Element ele2 = ele1.element("STATUS");
        Element ele3 = ele2.element("CODE");
        Element ele4 = ele2.element("SEVERITY");
        Element ele5 = ele1.element("DTSERVER");
        Element ele6 = root.element("SECURITIES_MSGSRSV1");
        if (!"0".equals(ele3.getText())) {           //第一层返回状态
            Element messageEle = ele2.element("MESSAGE");
            log.info(messageEle.getText());
            map.put("errMsg", messageEle.getText());
            return map;
        }
        Element ele7 = ele6.element("SCUSTSTMTTRNRS"); //第一层无错误才有第二层
        Element ele8 = ele7.element("TRNUID");          //交易唯一标识ID
        Element ele9 = ele7.element("STATUS");
        Element ele10 = ele9.element("CODE");
        if (!"0".equals(ele10.getText())) {           //返回第二层的状态
            Element messageEle = ele9.element("MESSAGE");
            map.put("errMsg", messageEle.getText());
            log.info(messageEle.getText());
            return map;
        }
        Element ele11 = ele7.element("SCUSTSTMTRS");
        Element ele12 = ele11.element("TRANLIST");

        String more = ele12.attributeValue("MORE");  //账户交易明细Y-还有下页流水，N-无下页流水。

        List<String> attacList = new ArrayList<String>();

        // 进行迭代；读取交易明细节点下的所有节点
        TransactionDetails transactionDetails = null;
        for (Iterator<Element> i = ele12.elementIterator(); i.hasNext(); ) {
            Element element = i.next();
            if ("STMTTRN".equals(element.getName())) {
                transactionDetails = new TransactionDetails();
                // 进行迭代；读取每条交易明细下的所有节点
                for (Iterator<Element> f = element.elementIterator(); f.hasNext(); ) {
                    Element elementc = f.next();
                    if ("CORRELATE_NAME".equals(elementc.getName())) {
                        transactionDetails.setCorrelateName((String) elementc.getData());   //对方账户名称
                    }
                    if ("CORRELATE_BANKNAME".equals(elementc.getName())) {
                        transactionDetails.setCorrelateBankname((String) elementc.getData());   //附加行名
                    }
                    if ("CORRELATE_BANKCODE".equals(elementc.getName())) {
                        transactionDetails.setCorrelateBankcode((String) elementc.getData());   //附加行号
                    }
                    if ("TRNAMT".equals(elementc.getName())) {
                        String trnamt = (String) elementc.getData();
                        transactionDetails.setTrnamt(Double.valueOf(trnamt));   //交易金额
                    }
                    if ("BALAMT".equals(elementc.getName())) {
                        String balamt = (String) elementc.getData();
                        transactionDetails.setBalamt(balamt);   //余额
                    }
                    if ("CORRELATE_ACCTID".equals(elementc.getName())) {

                        if (ToolUtil.isEmpty(elementc.getData())) {
                            break;
                        }
                        transactionDetails.setCorrelateAcctid((String) elementc.getData());  //对方账号
                    }
                    if ("TRNTYPE".equals(elementc.getName())) {
                        transactionDetails.setTrntype((String) elementc.getData());    //交易类型（借(DEBIT/CREDIT)贷类型）
                    }
                    if ("SRVRTID".equals(elementc.getName())) {
                        transactionDetails.setSrvrtid((String) elementc.getData());   //柜员流水号
                    }
                    if ("DTACCT".equals(elementc.getName())) {
                        transactionDetails.setDtacct((String) elementc.getData());   //记账日期
                    }
                    if ("CURRENCY".equals(elementc.getName())) {
                        transactionDetails.setCurrency((String) elementc.getData());   //币种
                    }
                    if ("MEMO".equals(elementc.getName())) {
                        transactionDetails.setMemo((String) elementc.getData());   //“摘要简称|用途
                    }
                    if ("BUSINESSTYPE".equals(elementc.getName())) {
                        transactionDetails.setBusinesstype((String) elementc.getData());   //业务类型
                    }
                    if ("ATTACHINFO".equals(elementc.getName())) {
                        transactionDetails.setAttachinfo((String) elementc.getData());
                        //流水唯一标识号，由流水交易日期、核心传票组序号、核心传票组内序号组成
                    }
                }
                transactionDetailslist.add(transactionDetails);

            }
        }
        //是否有下一页
        map.put("more", more);
        //交易流水信息集合
        map.put("transactionDetailList", transactionDetailslist);
        map.put("attacList", attacList);   //所有交易流水，用于更新平账状态
        return map;
    }

    /**
     * 根据交易流水查询是否存在此交易记录    true:存在  false:不存在
     * <p>
     * param attachinfo
     * return
     */
    private Boolean isCheck(String attachinfo) {
        TransactionDetails transactionDetails = new TransactionDetails();
        transactionDetails.setAttachinfo(attachinfo);
        List<TransactionDetails> list = transactionDetailsMapper.select(transactionDetails);
        return ToolUtil.isEmpty(list);
    }

    /**
     * 根据交易流水查询是否存在此交易记录（准备金打款流水表） true:存在  false:不存在
     *
     * @param attachinfo
     * @return
     */
    private Boolean isCheckReserveRecharge(String attachinfo) {
        ReserveRecharge reserveRecharge = new ReserveRecharge();
        reserveRecharge.setAttachinfo(attachinfo);
        List<ReserveRecharge> list = reserveRechargeMapper.select(reserveRecharge);
        return ToolUtil.isEmpty(list);
    }


    /**
     * 插入事务表-->更新在途总额-->插入在途明细-->查询账户余额--—>更新账户余额-->更新交易明细平账状态
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @SuppressWarnings("unchecked")
    public synchronized void reconciliationHandl(Map<String, Object> map, MchConfig mchConfigSupser, String status) {

        Long balance = (Long) map.get("balance");   //在途总额
        Long amoutCount = Long.valueOf(AmountUtil.convertDollar2Cent(String.valueOf(map.get("amoutCount"))));  //元转换成分
        List<String> list = (List<String>) map.get("attacList");
        insertDepositTra(mchConfigSupser.getMchNo());  //插入事物表
        if ("1".equals(status)) {   //在途1
            updateHisAccntOway(mchConfigSupser.getMchNo(), balance, amoutCount);
        } else {   //在途2
            onway2Update(mchConfigSupser.getMchNo(), amoutCount);  //在途2平账


        }
        TransactionDetails transactionDetails = new TransactionDetails();
        transactionDetails.setStatus("1");
        for (String listString : list) {
            transactionDetails.setAttachinfo(listString);
            transactionDetailsMapper.updateStatus(transactionDetails);    //更新交易记录的平账状态
        }
    }


    /**
     * 记录准备金历史交易明细
     */
    public void insertHisPrepay(String mchNo, Long amt, Long before, Long after) {

        HisAccntPrepay hisAccntPrepay = new HisAccntPrepay();
        hisAccntPrepay.setMchNo(mchNo);//商户号
        hisAccntPrepay.setOrderNo("");//订单号
        hisAccntPrepay.setTransactionNo("");//事务号
        hisAccntPrepay.setEvent(PayConstant.DEPOSIT_TRAN_EX_RECONCILIATION);
        hisAccntPrepay.setEventAmt(amt);//事件金额()
        hisAccntPrepay.setEventTime(new DateTime());
        hisAccntPrepay.setAccntAmtBefore(before);//事件发生前余额
        hisAccntPrepay.setAccntAmtAfter(after);//事件发生后金额
        hisAccntPrepay.setCreateTime(new DateTime());
        hisAccntPrepayMapper.insert(hisAccntPrepay);
    }


    /**
     * param
     * Description: 插入事务表
     * author wzc
     * date 2018/8/8 9:40
     */
    private void insertDepositTra(String mchNo) {

        DepositTransaction depositTransaction = new DepositTransaction();
        depositTransaction.setMchNo(mchNo);
        depositTransaction.setTransactionNo(UUID.randomUUID().toString().replace("-", "").toLowerCase());//事务号
        depositTransaction.setOutTransNo("0");//外部追踪号
        depositTransaction.setIdMchAccntChargeUp(0);
        depositTransaction.setCreateTime(new DateTime());//创建时间
        depositTransaction.setTransactionType(PayConstant.DEPOSIT_TRAN_EX_RECONCILIATION);//事务类型  平账
        depositTransaction.setUpdateTime(new DateTime());//更新时间
        depositTransactionMapper.insert(depositTransaction);
    }

    /**
     * 在途1账户平账逻辑
     * <p>
     * param
     * Description:
     * author wzc
     * date 2018/8/8 9:43
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateHisAccntOway(String mchNo, Long balance, Long res) {
        Long beforeMchAmt = 0L;
        MchAccnt mchAccntPre = new MchAccnt();
        mchAccntPre.setMchNo(mchNo);
        mchAccntPre.setAccntType(PayConstant.DEPOSIT_DEPOSITORY);  //存管户
        List<MchAccnt> mchAccntList = mchAccntMapper.selectMchAccnt(mchAccntPre);
        if (ToolUtil.isNotEmpty(mchAccntList)) {
            beforeMchAmt = mchAccntList.get(0).getRemainAmt();
        }

        HisAccntOnway hisAccntOnway = new HisAccntOnway();
        hisAccntOnway.setMchNo(mchNo);
        hisAccntOnway.setType("count");  //在途帐类型（count：总额，detailed :明细）
        hisAccntOnway.setBalance(res);   // 元转换为分
        hisAccntOnway.setUpdateTime(new Date());
        hisAccntOnwayMapper.updateOnway(hisAccntOnway);  //更新在途1总额

        MchAccnt mchAccnt2 = new MchAccnt();
        mchAccnt2.setAccntType(PayConstant.DEPOSIT_ONWAY);
        mchAccnt2.setMchNo(mchNo);
        mchAccnt2.setUpdateTime(new Date());
        mchAccnt2.setRemainAmt(res);
        mchAccntMapper.updateMchAmtNoZero(mchAccnt2);  //更新在途1账户余额

        hisAccntOnway.setEventTime(new Date());
        hisAccntOnway.setEvent(PayConstant.DEPOSIT_TRAN_EX_RECONCILIATION); //类型平账
        hisAccntOnway.setBalance(balance + res);  //在途总额
        hisAccntOnway.setEventAmt(res);  //事件发生金额
        hisAccntOnway.setAccntAmtBefore(balance);
        hisAccntOnway.setAccntAmtAfter(balance + res);  //发生后的金额
        hisAccntOnway.setCreateTime(new Date());
        hisAccntOnway.setUpdateTime(new Date());
        hisAccntOnway.setType("detailed");        //明细
        hisAccntOnwayMapper.insert(hisAccntOnway);   //插入在途明细

        MchAccnt mchAccnt = new MchAccnt();
        mchAccnt.setAccntType(PayConstant.DEPOSIT_DEPOSITORY);
        mchAccnt.setMchNo(mchNo);
        mchAccnt.setUpdateTime(new Date());
        mchAccnt.setRemainAmt(res);   //计算变更后余额
        mchAccntMapper.updateMchAmtNoZero(mchAccnt);  //更新存管户账户余额


        HisAccntMch hisMchAccnt = new HisAccntMch();
        hisMchAccnt.setMchNo(mchNo);//商户号
        hisMchAccnt.setOrderNo("");//订单号
        hisMchAccnt.setTransactionNo(UUID.randomUUID().toString().replace("-", "").toLowerCase());//事务号
        hisMchAccnt.setEvent(PayConstant.DEPOSIT_TRAN_PAYMENT);//类型支付到账
        hisMchAccnt.setEventAmt(res);//事件金额
        hisMchAccnt.setEventTime(new DateTime());
        hisMchAccnt.setAccntAmtBefore(beforeMchAmt);//事件发生前余额
        hisMchAccnt.setAccntAmtAfter(beforeMchAmt + res);//事件发生后金额
        hisMchAccnt.setCreateTime(new DateTime());
        hisAccntMchMapper.insert(hisMchAccnt); //插入存管户明细

        //2018.08.28 加入更新子商户实际可用余额
        MchAccnt mchAccnt1 = new MchAccnt();
        mchAccnt1.setMchNo(mchNo);
        mchAccnt1.setAccntType("countamt");
        List<MchAccnt> mchAccntList1 = mchAccntMapper.selectMchAccnt(mchAccnt1);
        Long countAmt = 0L;
        if (mchAccntList1.size() > 0) {
            countAmt = mchAccntList1.get(0).getRemainAmt();   //子商户实际可用余额
        }
        Long sunAmt = mchAccntMapper.selectSumAmt(mchAccnt1);   //子商户总余额
        //实际可用余额不能大于子商户总余额
        if (countAmt <= sunAmt) {
            //到账的钱+子商户实际可用余额大于子商户总余额 更新实际可用余额为子商户总余额
            if ((res + countAmt) > sunAmt) {
                mchAccnt1 = new MchAccnt();
                mchAccnt1.setAccntType("countamt");
                mchAccnt1.setRemainAmt(sunAmt);
                mchAccnt1.setUpdateTime(new Date());
                mchAccnt1.setMchNo(mchNo);
                mchAccntMapper.updateAccmount(mchAccnt1);
            } else {
                //更新实际可用余额为 ：到账的钱+实际可用余额
                mchAccnt1 = new MchAccnt();
                mchAccnt1.setAccntType("countamt");
                mchAccnt1.setRemainAmt(res);
                mchAccnt1.setUpdateTime(new Date());
                mchAccnt1.setMchNo(mchNo);
                mchAccntMapper.updateMchAmtNoZero(mchAccnt1);
            }
        }
    }


    /**
     * param
     * Description: 还准备金
     * author wzc
     * date 2018/8/30 20:09
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void timingPrepay(MchAccnt mchAccnt1) {
        //存管户
        //查询在途 子商户 分润的金额
        MchAccnt mchAccnt2 = new MchAccnt();
        mchAccnt2.setMchNo(mchAccnt1.getMchNo());
        int resAmt = mchAccntMapper.selectSumAll(mchAccnt2);
        if (mchAccnt1.getRemainAmt() > resAmt) {
            MchAccntChargeUp mchAccntChargeUp = new MchAccntChargeUp();
            mchAccntChargeUp.setMchNo(mchAccnt1.getMchNo());
            mchAccntChargeUp.setType("count");
            List<MchAccntChargeUp> mchAccntChargeUpList =
                    mchAccntChargeUpMapper.selectMchAccntChargeUp(mchAccntChargeUp);  //还准备金记录
            Long balnceCharge = 0L;    //要还准备金的钱
            if (mchAccntChargeUpList.size() > 0) {
                balnceCharge = mchAccntChargeUpList.get(0).getBalance();
            }
            //有还准备金的记录  金额<0  && 存管户的钱要大于要还的钱
            if (balnceCharge < 0 && mchAccnt1.getRemainAmt() > Math.abs(balnceCharge)) {

                String orderNo = MySeq.getNotifyTrans();

                String mchNo = mchAccnt1.getMchNo();

                MchAccnt accnt2 = new MchAccnt();
                accnt2.setMchNo(mchNo);//商户号
                accnt2.setAccntType("prepay");//准备金
                MchAccnt accnt3 = baseSelectMchAccnt(accnt2);//准备金账户信息（交易前）

                MchAccnt accnt = new MchAccnt();
                accnt.setMchNo(mchNo);//商户号
                accnt.setAccntType("depository");//存管户
                MchAccnt accnt1 = baseSelectMchAccnt(accnt);//存管户账户信息（交易前）

                MchAccnt mchAccntPre = new MchAccnt();
                mchAccntPre.setMchNo(mchNo);
                mchAccntPre.setAccntType("prepay");  //准备金
                mchAccntPre.setUpdateTime(new Date());
                mchAccntPre.setRemainAmt(Math.abs(balnceCharge));  //现有金额+要还的金额（取绝对值）
                mchAccntMapper.updateMchAmtNoZero(mchAccntPre);             //更新准备金余额

                //TODO 插入准备金历史记录表、存管户历史记录表
                HisAccntPrepay hisAccntPrepay = new HisAccntPrepay();
                hisAccntPrepay.setMchNo(mchNo);//商户号
                hisAccntPrepay.setOrderNo(orderNo);//订单号
                hisAccntPrepay.setTransactionNo("");//事务号
                hisAccntPrepay.setEvent(PayConstant.DEPOSIT_TRAN_EX_RECONCILIATION);//
                hisAccntPrepay.setEventAmt(Math.abs(balnceCharge));//事件金额(超出提现账户可用余额部分)
                hisAccntPrepay.setEventTime(new DateTime());
                hisAccntPrepay.setAccntAmtBefore(accnt3.getRemainAmt());//事件发生前余额
                hisAccntPrepay.setAccntAmtAfter(accnt3.getRemainAmt() - Math.abs(balnceCharge));//事件发生后金额
                hisAccntPrepay.setCreateTime(new DateTime());
                hisAccntPrepayMapper.insert(hisAccntPrepay);


                HisAccntMch hisMchAccnt = new HisAccntMch();
                hisMchAccnt.setMchNo(mchNo);//商户号
                hisMchAccnt.setOrderNo(orderNo);//订单号
                hisMchAccnt.setTransactionNo("");//事务号
                hisMchAccnt.setEvent(PayConstant.DEPOSIT_TRAN_EX_RECONCILIATION);//事件名称
                hisMchAccnt.setEventAmt(Math.abs(balnceCharge));//事件金额
                hisMchAccnt.setEventTime(new DateTime());
                hisMchAccnt.setAccntAmtBefore(accnt1.getRemainAmt());//事件发生前余额
                hisMchAccnt.setAccntAmtAfter(accnt1.getRemainAmt() - Math.abs(balnceCharge));//事件发生后金额
                hisMchAccnt.setCreateTime(new DateTime());
                hisAccntMchMapper.insert(hisMchAccnt);


                mchAccntPre = new MchAccnt();
                mchAccntPre.setMchNo(mchNo);
                mchAccntPre.setAccntType("depository");  //存管户
                mchAccntPre.setUpdateTime(new Date());
                mchAccntPre.setRemainAmt(balnceCharge);  //存管户金额=现有金额-要还的钱（取绝对值）
                mchAccntMapper.updateMchAmtNoZero(mchAccntPre);        //更新存管户余额

                mchAccntChargeUp = new MchAccntChargeUp();
                mchAccntChargeUp.setBalance(0L);
                mchAccntChargeUp.setMchNo(mchNo);
                mchAccntChargeUp.setType("count");
                mchAccntChargeUpMapper.updateChargeUp(mchAccntChargeUp);   //更新还准备金记录为0

                mchAccntChargeUp = mchAccntChargeUpList.get(0);
                mchAccntChargeUp.setIdMchAccntChargeUp(null);
                mchAccntChargeUp.setType("detailed");
                mchAccntChargeUp.setMchNo(mchNo);
                mchAccntChargeUp.setChargeUpTime(new Date());
                mchAccntChargeUp.setCreateTime(new Date());
                mchAccntChargeUp.setUpdateTime(new Date());
                mchAccntChargeUp.setStatu("1");
                mchAccntChargeUp.setChargeUpAmt(Math.abs(balnceCharge));
                mchAccntChargeUp.setBalance(0L);
                mchAccntChargeUpMapper.insert(mchAccntChargeUp);           //插入还准备金记录
            }
        }
    }


    /**
     * 更改在途2相关账户信息
     * <p>
     * param
     * Description:
     * author wzc
     * date 2018/8/8 9:50
     */
    @Transactional(rollbackFor = Exception.class)
    public void onway2Update(String mchNo, Long res) {

        HisAccntOnway2 hisAccntOnway2 = new HisAccntOnway2();
        hisAccntOnway2.setMchNo(mchNo);
        hisAccntOnway2.setType("count");  //在途帐类型（count：总额，detailed :明细）
        List<HisAccntOnway2> onway2List = hisAccntOnway2Mapper.selectHisAccntOnway2(hisAccntOnway2);  //查询在途2总额（超额提现总金额）
        Long balanceCharge = 0L;    //要还存管户的钱
        if (ToolUtil.isNotEmpty(onway2List)) {
            balanceCharge = onway2List.get(0).getBalance();
        }
        //直接还存管户
        //1.更改存管户余额，记存管户明细
        //2.平掉在途2总额（如果>0则置为0），记在途2明细
        //更改子商户可用总余额

        Long beforeMchAmt = 0L;
        MchAccnt mchAccntPre = new MchAccnt();
        mchAccntPre.setMchNo(mchNo);
        mchAccntPre.setAccntType(PayConstant.DEPOSIT_DEPOSITORY);  //存管户
        List<MchAccnt> mchAccntList = mchAccntMapper.selectMchAccnt(mchAccntPre);
        if (ToolUtil.isNotEmpty(mchAccntList)) {
            beforeMchAmt = mchAccntList.get(0).getRemainAmt();
        }

        mchAccntPre = new MchAccnt();
        mchAccntPre.setMchNo(mchNo);
        mchAccntPre.setAccntType(PayConstant.DEPOSIT_DEPOSITORY);  //存管户
        mchAccntPre.setUpdateTime(new Date());
        mchAccntPre.setRemainAmt(res);  //存管户金额=到账金额-要还的钱（取绝对值）
        mchAccntMapper.updateMchAmtNoZero(mchAccntPre);        //更新存管户余额

        HisAccntMch hisMchAccnt = new HisAccntMch();
        hisMchAccnt.setMchNo(mchNo);//商户号
        hisMchAccnt.setOrderNo("");//订单号
        hisMchAccnt.setTransactionNo(UUID.randomUUID().toString().replace("-", "").toLowerCase());//事务号
        hisMchAccnt.setEvent(PayConstant.DEPOSIT_TRAN_EX_PAYMENT);//类型超额提现到账
        hisMchAccnt.setEventAmt(res);//事件金额
        hisMchAccnt.setEventTime(new DateTime());
        hisMchAccnt.setAccntAmtBefore(beforeMchAmt);//事件发生前余额
        hisMchAccnt.setAccntAmtAfter(beforeMchAmt + res);//事件发生后金额
        hisMchAccnt.setCreateTime(new DateTime());
        hisAccntMchMapper.insert(hisMchAccnt); //插入存管户明细


        HisAccntOnway2 hisAccntOnway21 = new HisAccntOnway2();
        hisAccntOnway21.setMchNo(mchNo);
        hisAccntOnway21.setType("count");  //在途帐类型（count：总额，detailed :明细）
        hisAccntOnway21.setBalance(balanceCharge + res);   // 元转换为分
        hisAccntOnway21.setUpdateTime(new Date());
        hisAccntOnway2Mapper.updateOnway2(hisAccntOnway21);  //更新在途2总额

        hisAccntOnway21 = new HisAccntOnway2();
        hisAccntOnway21.setMchNo(mchNo);

        hisAccntOnway2.setEventTime(new Date());
        hisAccntOnway2.setEvent(PayConstant.DEPOSIT_TRAN_EX_RECONCILIATION); //类型平账
        hisAccntOnway2.setBalance(res);  //平账金额
        hisAccntOnway2.setEventAmt(res);  //事件发生金额
        hisAccntOnway2.setAccntAmtBefore(balanceCharge);
        hisAccntOnway2.setAccntAmtAfter(balanceCharge + res);  //发生后的金额
        hisAccntOnway2.setCreateTime(new Date());
        hisAccntOnway2.setUpdateTime(new Date());
        hisAccntOnway2.setType("detailed");        //明细
        hisAccntOnway2Mapper.insert(hisAccntOnway2);   //插入在途2明细


        MchAccnt mchAccnt1 = new MchAccnt();
        mchAccnt1.setMchNo(mchNo);
        mchAccnt1.setAccntType("countamt");
        List<MchAccnt> mchAccntList1 = mchAccntMapper.selectMchAccnt(mchAccnt1);
        Long countAmt = 0L;
        if (mchAccntList1.size() > 0) {
            countAmt = mchAccntList1.get(0).getRemainAmt();   //子商户实际可用余额
        }
        Long sunAmt = mchAccntMapper.selectSumAmt(mchAccnt1);   //子商户总余额
        if (countAmt <= sunAmt) {
            //到账的钱+子商户实际可用余额大于子商户总余额 更新实际可用余额为子商户总余额
            if ((res + countAmt) > sunAmt) {
                mchAccnt1 = new MchAccnt();
                mchAccnt1.setAccntType("countamt");
                mchAccnt1.setRemainAmt(sunAmt);
                mchAccnt1.setUpdateTime(new Date());
                mchAccnt1.setMchNo(mchNo);
                mchAccntMapper.updateAccmount(mchAccnt1);
            } else {
                //更新实际可用余额为 ：到账的钱+实际可用余额
                mchAccnt1 = new MchAccnt();
                mchAccnt1.setAccntType("countamt");
                mchAccnt1.setRemainAmt(res);
                mchAccnt1.setUpdateTime(new Date());
                mchAccnt1.setMchNo(mchNo);
                mchAccntMapper.updateMchAmtNoZero(mchAccnt1);
            }
        }

    }


    /**
     * 在途1定时平账
     * <p>
     * param lastWordDay
     * param today
     * param mchConfigSupser
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @SuppressWarnings("unchecked")
    public void reconciliationOnway(String lastWordDay, String today, MchConfig mchConfigSupser) {
        String mchNo = mchConfigSupser.getMchNo();
        String versionNo = mchConfigSupser.getVersionNo();
        //查询上一个工作日的在途1总额
        //发送交易记录查询（初始页数为1，type为1代表来账）
        Map<String, Object> res = doPost(today, today, 1, 1, mchConfigSupser, "在途1平账");
        if (ToolUtil.isEmpty(res) || ToolUtil.isNotEmpty(res.get("errMsg"))) {
            log.info(mchConfigSupser.getMchNo() + "---在途1平账查询流水:" + res.get("errMsg"));
            log.info(res.toString());
            return;
        }
        List<TransactionDetails> transactionDetailsList = (ArrayList<TransactionDetails>) res.get(
                "transactionDetailList");
        Mch mch = new Mch();
        mch.setMchNo(mchConfigSupser.getMchNo());
        mch.setChannel("payment");  //支付通道卡号
        //查询商户的结算通道卡号
        List<Mch> mchList = mchMapper.selectMch(mch);
        List<String> bankcardList = new ArrayList<>();
        for (Mch mch1 : mchList){
            bankcardList.add(mch1.getBankcard());
        }
        Double amtCount = 0d;
        List<String> attacList = new ArrayList<>();
        for (TransactionDetails transactionDetails : transactionDetailsList) {
            String corrAcctId = transactionDetails.getCorrelateAcctid();//付款卡号
            String attachInfo = transactionDetails.getAttachinfo();//银行唯一交易流水号
            Double trnamt = transactionDetails.getTrnamt();//交易金额

            //如果流水记录的付款卡号 = 配置表的通道结算卡号，且没有计算过
            if (bankcardList.contains(corrAcctId)) {
                if (isCheck(attachInfo)) {
                    amtCount = new BigDecimal(Double.toString(trnamt)).add(new BigDecimal(Double.toString(amtCount))).doubleValue();
                    //匹配过的信息，记录到平账记录表
                    transactionDetails.setType("onway");
                    transactionDetails.setMchno(mchConfigSupser.getMchNo());
                    transactionDetailsMapper.insert(transactionDetails);
                    attacList.add(transactionDetails.getAttachinfo());
                }
            } else {
                if (isCheckReserveRecharge(attachInfo)) {
                    ReserveRecharge reserveRecharge = new ReserveRecharge();
                    BeanUtils.copyProperties(transactionDetails, reserveRecharge);
                    reserveRecharge.setMchno(mchConfigSupser.getMchNo());
                    reserveRecharge.setStatus("0");
                    reserveRecharge.setCreateTime(new Date());
                    reserveRechargeMapper.insert(reserveRecharge);
                }
            }
            res.put("amoutCount", amtCount);
        }
        Long balance = 0L;
        //查询上一个工作日到昨天的在途总额（例如：今天周一，上周五，周末的支付及在途，全部在今天结算到账；   今天10月8号，9.30-10.7日的支付，全部在10.8号结算到账）
        HisAccntOnway hisAccntOnway = iHisAccntOnwayService.selectCountOnwayByTime(mchNo, lastWordDay, today,versionNo);
        if (ToolUtil.isNotEmpty(hisAccntOnway) && ToolUtil.isNotEmpty(hisAccntOnway.getEventAmt())) {
            balance = hisAccntOnway.getEventAmt();
            log.info("\n 查询到【" + mchNo + "】：在途总额为" + balance);
        } else {
            log.info("\n 查询到【" + mchNo + "】：在途总额为" + balance);
            return;
        }
        if ((Double) res.get("amoutCount") > 0) {    //有入账金额
            res.put("attacList", attacList);
            res.put("balance", balance);
            reconciliationHandl(res, mchConfigSupser, "1");   //平账逻辑
            log.info(cn.hutool.core.date.DateUtil.now() + " 【定时平账】[" + mchConfigSupser.getMchNo() + "]在途1定时平账成功平账金额：" + res.get("amoutCount"));
        } else {
            log.info(cn.hutool.core.date.DateUtil.now() + " 【定时平账】[" + mchConfigSupser.getMchNo() + "]在途1" +
                    "定时平账未检测到有入账金额");
        }
    }


    /**
     * 在途2定时平账
     * <p>
     * param lastday
     * param today
     * param mchConfigSupser
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @SuppressWarnings("unchecked")
    public void reconciliationOnway2(String lastday, String today, MchConfig mchConfigSupser) {
        String date = cn.hutool.core.date.DateUtil.today();
        Map<String, Object> res = doPost(date, date, 1, 1, mchConfigSupser, "在途2平账");
        if (ToolUtil.isNotEmpty(res.get("errMsg"))) {
            log.info(mchConfigSupser.getMchNo() + "---在途2平账查询流水:" + res.get("errMsg"));
            return;
        }
        List<TransactionDetails> transactionDetailsList = (List<TransactionDetails>) res.get("transactionDetailList");
        Mch mch = new Mch();
        mch.setMchNo(mchConfigSupser.getMchNo());
        mch.setChannel("excess");  //超额提现
        //查询商户的超额提现卡号
        List<Mch> mchList = mchMapper.selectMch(mch);
        List<String> bankcardList = new ArrayList<>();
        for (Mch mch1 : mchList){
            bankcardList.add(mch1.getBankcard());
        }
        Double amtCount = 0d;
        List<String> attacList = new ArrayList<>();
        for (TransactionDetails transactionDetails : transactionDetailsList) {
            String corrAcctId = transactionDetails.getCorrelateAcctid();//付款卡号
            String attachInfo = transactionDetails.getAttachinfo();//银行唯一交易流水号
            Double trnamt = transactionDetails.getTrnamt();//交易金额

            //如果流水记录的付款卡号 = 配置表的通道结算卡号，且没有计算过
            if (bankcardList.contains(corrAcctId)) {
                if (isCheck(attachInfo)) {
                    amtCount = new BigDecimal(Double.toString(trnamt)).add(new BigDecimal(Double.toString(amtCount))).doubleValue();
                    //匹配过的信息，记录到平账记录表
                    transactionDetails.setType(PayConstant.DEPOSIT_ONWAY2);
                    transactionDetails.setMchno(mchConfigSupser.getMchNo());
                    transactionDetailsMapper.insert(transactionDetails);
                    attacList.add(transactionDetails.getAttachinfo());
                }
            } else {
                if (isCheckReserveRecharge(attachInfo)) {
                    ReserveRecharge reserveRecharge = new ReserveRecharge();
                    BeanUtils.copyProperties(transactionDetails, reserveRecharge);
                    reserveRecharge.setMchno(mchConfigSupser.getMchNo());
                    reserveRecharge.setStatus("0");
                    reserveRecharge.setCreateTime(new Date());
                    reserveRechargeMapper.insert(reserveRecharge);
                }
            }

            res.put("amoutCount", amtCount);
        }

        HisAccntOnway2 hisAccntOnway2 = new HisAccntOnway2();
        hisAccntOnway2.setMchNo(mchConfigSupser.getMchNo());
        hisAccntOnway2.setType("count");  //在途帐类型（count：总额，detailed :明细）
        List<HisAccntOnway2> hisAccntOnway2List = iHisAccntOnway2Service.selectHisAccntOnway2(hisAccntOnway2);
        //查在途2的总额
        Long balance;
        if (hisAccntOnway2List.size() > 0) {
            balance = hisAccntOnway2List.get(0).getBalance();
        } else {
            log.info("【定时平账】在途2定时平账失败，错误信息：未查询到在途2记录！");
            return;
        }
        if (amtCount > 0) {    //有入账金额
            res.put("attacList", attacList);
            res.put("balance", balance);
            reconciliationHandl(res, mchConfigSupser, "2");   //平账逻辑
            log.info(cn.hutool.core.date.DateUtil.now() + " 【定时平账】[" + mchConfigSupser.getMchNo() + "]在途2定时平账成功平账金额：" + res.get("amoutCount"));
        } else {
            log.info(cn.hutool.core.date.DateUtil.now() + " 【定时平账】[" + mchConfigSupser.getMchNo() + "]在途2" +
                    "定时平账未检测到有入账金额");
        }
    }

    public MchAccnt baseSelectMchAccnt(MchAccnt mchAccnt) {
        List<MchAccnt> list = mchAccntMapper.selectMchAccnt(mchAccnt);
        if (list.size() > 0 && list.get(0) != null) {
            return list.get(0);
        }
        return null;
    }


    /**
     * 获取对账单
     *
     * @param billDate
     * @param mchConfig
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void getDownload(String billDate, MchConfig mchConfig) throws Exception {
        if (ToolUtil.isEmpty(mchConfig.getGetDownloadUrl())) {
            log.info(mchConfig.getMchNo() + "没有获取对账单配置");
            return;
        }
        //请求获取对账单接口
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("service", "get_download_request");
        paramMap.put("partner", mchConfig.getPartner());
        paramMap.put("time", org.com.yz.common.util.DateUtil.getCurrentDate());
        paramMap.put("billDate", billDate);
        paramMap.put("md5_key", mchConfig.getMd5Key());

        String sign = ToolUtil.paramSign(paramMap, mchConfig.getMd5Key());
        System.out.println("\n请求签名为：" + sign);
        paramMap.put("sign", sign);

        String json = ToolUtil.txn(mchConfig.getGetDownloadUrl(), paramMap, "");//发送请求
//        String json = "{\n" +
//                "    \"result\": {\n" +
//                "        \"msg\": \"成功\",\n" +
//                "        \"code\": \"0\"\n" +
//                "    },\n" +
//                "    \"items\": [\n" +
//                "        {\n" +
//                "            \"key\": \"258799714\",\n" +
//                "            \"appId\": \"MH20181203135017GO5r\",\n" +
//                "            \"requestUrl\": \"http://allpaytest.visastandards.com/gclients\",\n" +
//                "            \"method\": \"POST\",\n" +
//                "            \"params\": \"{\\\"data\\\":\\\"{\\\\\\\"version\\\\\\\":\\\\\\\"2.0\\\\\\\"," +
//                "\\\\\\\"merchant_id\\\\\\\":\\\\\\\"86570020\\\\\\\"," +
//                "\\\\\\\"timestamp\\\\\\\":\\\\\\\"20190104173215\\\\\\\",\\\\\\\"biz_type\\\\\\\":\\\\\\\"wx" +
//                ".billdownload\\\\\\\",\\\\\\\"biz_content\\\\\\\":{\\\\\\\"mch_type\\\\\\\":\\\\\\\"pay.bill" +
//                ".merchant\\\\\\\",\\\\\\\"bill_date\\\\\\\":\\\\\\\"20190103\\\\\\\"}," +
//                "\\\\\\\"sign_type\\\\\\\":\\\\\\\"MD5\\\\\\\"}\\\"," +
//                "\\\"sign\\\":\\\"2645422f661aa510aee1a7fd4bd8fdb3\\\"}\",\n" +
//                "            \"code\": \"0\",\n" +
//                "            \"msg\": \"成功\"\n" +
//                "        },\n" +
//                "        {\n" +
//                "            \"key\": \"1501900631\",\n" +
//                "            \"appId\": \"wxdef3e972a4a93e91\",\n" +
//                "            \"requestUrl\": \"https://api.mch.weixin.qq.com/pay/downloadbill\",\n" +
//                "            \"method\": \"POST\",\n" +
//                "            \"params\": \"{data:'<?xml version=\\\"1.0\\\" " +
//                "encoding=\\\"UTF-8\\\"?><xml><nonce_str>8a8080a1681834bc01681834bccb0000</nonce_str><bill_date" +
//                ">20190103</bill_date><appid>wxdef3e972a4a93e91</appid><sign>0C3B3E8CE8A1F9BEC762CE24E787D469</sign" +
//                "><bill_type>ALL</bill_type><mch_id>1501900631</mch_id></xml>', " +
//                "sign:'0C3B3E8CE8A1F9BEC762CE24E787D469'}\",\n" +
//                "            \"code\": \"0\",\n" +
//                "            \"msg\": \"成功\"\n" +
//                "        }\n" +
//                "    ]\n" +
//                "}";
        System.out.println(json);
        //jsonString 转 jsonObject
        JSONObject po = JSONObject.parseObject(json);
        //捕获超时
        if (StringUtils.isNotEmpty((String) po.get("errcode"))) {
            log.error(po.toJSONString());
            return;
        }

        //返回data
        String resultData = po.getString("result");
        String resultItems = po.getString("items");

        if (!"0".equals(JSONObject.parseObject(resultData).getString("code"))) {
            System.out.println("\n请求失败:" + JSONObject.parseObject(resultData).getString("code") + "---" + JSONObject.parseObject(resultData).getString("msg"));
        } else {
            System.out.println("\n请求成功");
        }
        System.out.println("\n 返回报文为：" + XmlFormatter.formatJson(po.toJSONString()));

        JSONArray jsonArray = JSON.parseArray(resultItems);
        if (jsonArray.size() > 0) {
            for (int i = 0; i < jsonArray.size(); i++) {
                // 遍历 jsonarray 数组，把每一个对象转成 json 对象
                JSONObject job = jsonArray.getJSONObject(i);
                String key = job.getString("key");
                String appId = job.getString("appId");
                String requestUrl = job.getString("requestUrl");
                String method = job.getString("method");
                String params = job.getString("params");
                String downloadUrl = job.getString("downloadUrl");

                //查询对账单是否已经下载过
                DownloadInfo download = new DownloadInfo();
                download.setBillDate(billDate);
                download.setMchNo(mchConfig.getMchNo());
                download.setChannel(key);
                DownloadInfo downloadInfo = downloadInfoMapper.selectDownloadInfo(download);

                if (downloadInfo != null) {
                    log.info(mchConfig.getMchNo() + "对账单已获取" + " 渠道:" + key + " 时间: " + billDate);
                    continue;
                }

                //查询mch是否有配置过改通道的key
                Mch mch = new Mch();
                mch.setMchNo(mchConfig.getMchNo());
                mch.setAccMchId(key);
                Mch mch1 = mchMapper.selectMchByAccMchId(mch);
                if (mch1 == null) {
                    log.info(mchConfig.getMchNo() + "未匹配到与下载对账单相对应的key" + " 渠道:" + key + " 时间: " + billDate);
                    continue;
                }
                String accType = mch1.getAccType();

                //请求支付宝，微信，获取对账单下载url
                String billDownloadUrl = billDownloadUrl(method, params, requestUrl, downloadUrl,accType,key,billDate,mchConfig);

                //获取对账单并对账---->给出对账结果
                if (billDownloadUrl != null) {
                    downloadFileInfo(billDownloadUrl, mchConfig, billDate, method, key);
                } else {
                    System.out.println("\n 拉取对账单url为空：" + billDownloadUrl);
                }
            }
        }
    }

    /**
     * 请求支付宝，微信，获取对账单下载url
     *
     * @param method
     * @param params
     * @param requestUrl
     * @param downloadUrl
     * @return
     */
    private String billDownloadUrl(String method, String params, String requestUrl, String downloadUrl,String accType,String key,String billDate,MchConfig mchConfig) throws Exception {
        String resData = null,billDownloadUrl = null;
        //请求支付宝，微信，获取对账单下载url
        if ("POST".equals(method)) {
            String fileName = null;
            if ("wxgf".equals(accType)) {
                fileName = uploadWxXML(requestUrl, params);
                if (fileName != null && fileName != ""){
                    //将获取到的url及对应通道 插入对账单数据表中
                    DownloadInfo downloadInfo = new DownloadInfo(mchConfig.getMchNo(), key, fileName, billDate, new DateTime(), new DateTime());
                    int flag = downloadInfoMapper.insert(downloadInfo);
                    if (flag > 0) {
                        log.info("获取对账单成功");
                    }
                }
            } else if ("wx".equals(accType)){
                //其他未知支付渠道
                Map mapObj = JSONObject.parseObject(params, Map.class);
                resData = cn.hutool.http.HttpUtil.post(requestUrl, mapObj);
                log.info(resData);
                JSONObject jsonObject = JSONObject.parseObject(resData);
                String jsonData = jsonObject.getString("data");
                JSONObject res = JSONObject.parseObject(jsonData);
                String bizcontent = res.getString("biz_content");
                JSONObject bizObj = JSONObject.parseObject(bizcontent);
                billDownloadUrl = bizObj.getString("bill_url");

                billDownloadUrl = "http://bill.visastandards.com/"+billDate+"/86570015-"+billDate+"-ALL-pay.bill.merchant.csv";
            }
            System.out.println("POST请求地址为：" + billDownloadUrl);
        } else if ("GET".equals(method)) {
            resData = HttpUtil.doGet(downloadUrl, "GBK");
            JSONObject jsonObject = JSONObject.parseObject(resData);
            JSONObject res = JSONObject.parseObject(jsonObject.getString("alipay_data_dataservice_bill_downloadurl_query_response"));
            billDownloadUrl = res.getString("bill_download_url");
            System.out.println("请求地址为：" + billDownloadUrl);
        }
        log.info(resData);
        return billDownloadUrl;
    }

    /**
     * @描述: 解析微信官方对账单，并上传
     * @参数:
     * @返回:
     * @作者: wzc
     * @时间: 2019-01-05 10:20
     */
    public String uploadWxXML(String requestUrl, String params) throws Exception {
        //创建请求客户端
        HttpClient client = HttpClientBuilder.create().build();
        //拼装请求xml并组装请求
        HttpPost request = new HttpPost(requestUrl);
        JSONObject jsonData = JSONObject.parseObject(params);
        String data = jsonData.getString("data");
        ByteArrayEntity requestEntity = null;
        log.info("微信官方对账单请求报文如下：{}", XmlFormatter.formatXml(data));
        try {
            requestEntity = new ByteArrayEntity(data.trim().getBytes("gbk"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            log.error("xml写入请求编码错误");
        }
        request.setEntity(requestEntity);
        request.setHeader("Content-Type", "application/x-fox");
        request.setHeader("Intensoft-Request-Sign", "YES");
        //发送请求并提取响应
        HttpResponse response = null;
        response = client.execute(request);
        System.out.println("微信官方对账单响应报文结果：" + response.getStatusLine().getStatusCode()+"  数据:"+response.getEntity().getContent());
        //创建xml读取器并获取xml文件
        InputStreamReader reader = new InputStreamReader(response.getEntity().getContent(), "utf-8");
        BufferedReader bufferedReader = new BufferedReader(reader);
        String path = "";
        //设定为当前文件夹
        File directory = new File("E:\\log");

        //获取标准的路径
        System.out.println(directory.getCanonicalPath()+"/file/");

        String projectPath = directory.getAbsolutePath();
        //生成ftp文件名
        String fileName = RandomNum.createRandomString(20) + ".csv";

        path = projectPath+"/"+fileName;

        CsvUtils.toConversionCSV(path,bufferedReader);

        return uploadFileToFTP(projectPath, fileName);
    }

    /**
     * @描述: 解析威富通微信对账单，并上传
     * @参数:
     * @返回:
     * @作者: wzc
     * @时间: 2019-01-05 10:20
     */
    public String uploadWxWftXML(String requestUrl, String params) throws Exception {
        //创建请求客户端
        HttpClient client = HttpClientBuilder.create().build();
        //拼装请求xml并组装请求
        HttpPost request = new HttpPost(requestUrl);
        ByteArrayEntity requestEntity = null;
        log.info("威富通微信对账单请求报文如下：{}", XmlFormatter.formatXml(params));
        try {
            requestEntity = new ByteArrayEntity(params.trim().getBytes("gbk"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            log.error("xml写入请求编码错误");
        }
        request.setEntity(requestEntity);
        request.setHeader("Content-Type", "application/x-fox");
        request.setHeader("Intensoft-Request-Sign", "YES");
        //发送请求并提取响应
        HttpResponse response = null;
        response = client.execute(request);
        System.out.println("威富通微信对账单响应报文结果：" + response.getStatusLine().getStatusCode()+"  数据:"+response.getEntity().getContent());
        //创建xml读取器并获取xml文件
        InputStreamReader reader = new InputStreamReader(response.getEntity().getContent(), "utf-8");
        BufferedReader bufferedReader = new BufferedReader(reader);
        String path = "";
        //设定为当前文件夹
        File directory = new File("E:\\log");

        //获取标准的路径
        System.out.println(directory.getCanonicalPath()+"/file/");

        String projectPath = directory.getAbsolutePath();
        //生成ftp文件名
        String fileName = RandomNum.createRandomString(20) + ".csv";

        path = projectPath+"/"+fileName;

        CsvUtils.toConversionCSV(path,bufferedReader);

        return uploadFileToFTP(projectPath, fileName);
    }


    /**
     * 威富通下载微信对账单
     * @param billDate
     * @param mchId
     * @return
     * @throws Exception
     */
    private String downloadFileByWft(String billDate,String mchId) throws Exception {
        String xml = "<xml>" +
                "  <service>pay.bill.merchant</service>" +
                "  <bill_date>"+billDate+"</bill_date>" +
                "  <bill_type>ALL</bill_type>" +
                "  <mch_id>"+mchId+"</mch_id>" +
                "  <nonce_str>"+MySeq.getTrans()+"</nonce_str>" +
                "</xml>";
        Map<String, String> map = WXPayUtil.xmlToMap(xml);

        String params = WXPayUtil.generateSignedXml(map,"417920bb274f2ac878649fcd40375a13");

        return params;
    }


    /**
     * 获取对账单并对账---->给出对账结果
     *
     * @param billDownloadUrl
     * @param mchConfig
     * @param billDate
     * @param method
     * @param key
     */
    private void downloadFileInfo(String billDownloadUrl, MchConfig mchConfig, String billDate, String method, String key) {
        //文件下载 并进行对账逻辑处理
        String fileName = downloadFile(billDownloadUrl, billDate, method, mchConfig, key);
        if (ToolUtil.isEmpty(fileName)) {
            log.info("上传文件失败");
        } else {
            //将获取到的url及对应通道 插入对账单数据表中
            DownloadInfo downloadInfo = new DownloadInfo(mchConfig.getMchNo(), key, fileName, billDate, new DateTime(), new DateTime());

            int flag = downloadInfoMapper.insert(downloadInfo);
            if (flag > 0) {
                log.info("获取对账单成功");
            }
        }
    }


    /**
     * 文件下载
     *
     * @param downloadUrl
     * @param billDate
     * @param method
     * @return
     */
    public String downloadFile(String downloadUrl, String billDate, String method, MchConfig mchConfig, String key) {
        //下载文件
        String projectPath = "";
        String fileNames = "";


        Mch mch = new Mch();
        mch.setMchNo(mchConfig.getMchNo());
        mch.setAccMchId(key);
        Mch mch1 = mchMapper.selectMchByAccMchId(mch);

        if (mch1 == null) {
            System.out.println("未匹配到与下载对账单相对应的key");
            return null;
        }

        //渠道类型
        String accType = mch1.getAccType();

        if ("zfb".equals(accType)) {
            fileNames = billDate + "zfb_dzd.zip";
        } else if ("wx".equals(accType)) {
            fileNames = billDate + "wx_dzd.csv";
        }

        try {
            //设定为当前文件夹
            File directory = new File("E:\\log");
            //获取标准的路径
            System.out.println(directory.getCanonicalPath()+"/file/");
            projectPath = directory.getAbsolutePath();

            File file = downLoadFromUrl(downloadUrl, fileNames, "/" + projectPath + "/");

            if (file != null) {
                if ("zfb".equals(accType)) {
                    //解压zip获取csv对账单数据
                    String nameCSVDetail = ZipUtils.unZipFiles(file, projectPath + "/");
                    //过滤csv数据使得csv中数据适用用于数据正常读取
                    CsvUtils.toConversionCSV(nameCSVDetail);
                    File resFile = new File(nameCSVDetail);
                    fileNames = resFile.getName();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println(e);
        }

        //上传对账单到ftp
        return uploadFileToFTP(projectPath, fileNames);

    }

    /**
     * 对账
     *
     * @param mchConfig
     * @param type
     * @param detail_income
     * @param detail_payout
     * @param timeAdd
     */
    private void dispatch(MchConfig mchConfig, String type, String detail_income, String detail_payout, String timeAdd) {

        String token = "";

        Map<String, String> bizContentMap = new HashMap<>();
        bizContentMap.put("mch_accnt_no", "T0020181031094411000000");
        bizContentMap.put("order_no", "T00"+ToolUtil.getRandomString(18));
        bizContentMap.put("trans_no", ToolUtil.getRandomString(5));
        bizContentMap.put("dispatch_event", "pay");
        //业务数据
        bizContentMap.put("dispatch_type", "1");
        bizContentMap.put("amount","100");
        bizContentMap.put("trans_time",DateUtil.getDay());
        bizContentMap.put("trans_channel","wx");

        List  list = new ArrayList();
        list.add(bizContentMap);
        Map<String,Object> accntDetaiMap = new HashMap<>();
        accntDetaiMap.put("split_accnt_detail",list);

        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("biz_type", "mchaccnt.dispatch");
        dataMap.put("mch_no", "T888");
        dataMap.put("sign_type", "MD5");
        dataMap.put("timestamp", DateUtil.getCurrentDate());
        dataMap.put("biz_content", accntDetaiMap);
        //公共参数
        dataMap.put("out_trans_no", "R00001");

        String data = JSON.toJSONString(dataMap);
        HashMap<String, String> params = new HashMap<String, String>();
        //计算签名
        String sign = PayDigestUtil.md5(data + "&" + dataMap.get("timestamp") + token, "UTF-8");
        params.put("data", data);
        //组装请求参数
        params.put("sign", sign);
        String json = null;
        //发送请求
//        json = txn("http://wzz.visastandards.com/api/deposit.htm", params, "");
        //jsonString 转 jsonObject
        System.out.println(json);

    }

    public static String txn(String url, Map<String, String> params, String mchNo) {

        String json = null;
        try {
            IRequestService requestor = IRequestService.getInstance();
            json = (String) requestor.sendPost(url, params);
        } catch (IOException e) {
            json = ConfigureTest.TXN_ERROR_RESULT;
        } catch (Exception e) {
            json = ConfigureTest.SYS_ERROR_RESULT;
        }
        return json;
    }

    /**
     * 上传对账单到ftp
     *
     * @param projectPath
     * @param fileNames
     * @return
     */
    private String uploadFileToFTP(String projectPath, String fileNames) {
        String ftpHost = "116.62.144.118";
        String ftpUserName = "marcus";
        String ftpPassword = "zjYinZhun@asdhuez#4@(_9";

        boolean test = false;

        int ftpPort = 21;
        //ftp目录
        String ftpPath = "deposit/";
        //本地文件地址
        String localPath = projectPath + "/" + fileNames;
        //生成ftp文件名
        String fileName = RandomNum.createRandomString(20) + ".csv";
        //上传一个文件
        try {
            FileInputStream in = new FileInputStream(new File(localPath));
            test = FtpUtil.uploadFile(ftpHost, ftpUserName, ftpPassword, ftpPort, ftpPath, fileName, in);
            System.out.println(test);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            System.out.println(e);
        }

        //删除文件
        File file = new File(localPath);
        file.delete();

        if (test) {
            return fileName;
        } else {
            return null;
        }
    }

    /**
     * 下载对账单到localPath
     *
     * @param projectPath
     * @param fileNames
     * @return
     */
    private File downloadFileToFTP(String projectPath, String fileNames) {
        String ftpHost = "116.62.144.118";
        String ftpUserName = "marcus";
        String ftpPassword = "zjYinZhun@asdhuez#4@(_9";
        int ftpPort = 21;
        //ftp目录
        String ftpPath = "deposit/";
        //本地文件地址
        String localPath = projectPath + "/";
        //上传一个文件
        File file = FtpUtil.downloadFtpFile(ftpHost, ftpUserName, ftpPassword, ftpPort, ftpPath, localPath, fileNames);
        System.out.println(file);
        return file;
    }


    /**
     * 从网络Url中下载文件
     *
     * @param urlStr
     * @param fileName
     * @param savePath
     * @throws IOException
     */
    public static File downLoadFromUrl(String urlStr, String fileName, String savePath) throws IOException {
        URL url = new URL(urlStr);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        //设置超时间为3秒
        conn.setConnectTimeout(3 * 1000);
        //防止屏蔽程序抓取而返回403错误
        conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");

        //得到输入流
        InputStream inputStream = conn.getInputStream();
        //获取自己数组
        byte[] getData = readInputStream(inputStream);

        //文件保存位置
        File saveDir = new File(savePath);
        if (!saveDir.exists()) {
            saveDir.mkdir();
        }
        File file = new File(saveDir + File.separator + fileName);
        FileOutputStream fos = new FileOutputStream(file);
        fos.write(getData);
        if (fos != null) {
            fos.close();
        }
        if (inputStream != null) {
            inputStream.close();
        }


        System.out.println("info:" + url + " download success");

        return file;
    }

    /**
     * 微信/支付宝/其他通道处理逻辑（合并对账单中同一订单号）
     * @param accountDetails
     * @return
     */
    public Map<String, Object> halder(AccountDetails accountDetails,List<AccountDetailsFile> list,String type) {

        int accCount = 0;
        int reconCunt = 0;
        Map<String, Object> map = new HashMap<>();
        //返回用--->未匹配的分账记录
        List<AccountDetails> noacclist = new ArrayList<>();
        //返回用--->未匹配的对账单
        List<AccountDetailsFile> nofilelist = new ArrayList<>();

        //目前的分账明细
        List<AccountDetails> accountDetailList = accountDetailsMapper.selectFileList(accountDetails);
        if (accountDetailList.size() <= 0) {
            return null;
        }

        //先处理导入的对账明细---平台订单号是否重复  y--合并
        for (AccountDetailsFile accountDetailsFile : list) {
            if (ToolUtil.isEmpty(accountDetailsFile)) {
                break;
            }
            //查询map有没有此订单号
            AccountDetailsFile accountDetailsFile1 =
                    (AccountDetailsFile) map.get(accountDetailsFile.getPlatform_order_number().replaceAll("`", ""));
            if (ToolUtil.isNotEmpty(accountDetailsFile1)) {
                //已经存在  --->合并
                String amtOld = AmountUtil.convertDollar2Cent(accountDetailsFile1.getTotal_sum().replaceAll("`", ""));
                int amtInt = (int) Integer.valueOf(String.valueOf(amtOld));
                //现在的  出现第二笔应该为退款  取退款金额   计负数
                String amtNew = AmountUtil.convertDollar2Cent(accountDetailsFile.getRefund_amount().replaceAll("`",
                        ""));
                int amtNewInt = 0;
                if ("转入退款".equals(accountDetailsFile.getTrading_status().replaceAll("`", ""))) {
                    amtNewInt = -(int) Integer.valueOf(String.valueOf(amtNew));
                } else {
                    amtNewInt = (int) Integer.valueOf(String.valueOf(amtNew));
                }

                String chargeAmtOld = AmountUtil.convertDollar2Cent(accountDetailsFile1.getService_charge().replaceAll("`", ""));
                int chargeAmInt = (int) Integer.valueOf(String.valueOf(chargeAmtOld));
                //现在的  出现第二笔应该为退款  取退款金额   计负数
                String chargeAmNew = AmountUtil.convertDollar2Cent(accountDetailsFile.getService_charge().replaceAll("`",
                        ""));
                int chargeAmNewInt = 0;
                if ("转入退款".equals(accountDetailsFile.getTrading_status().replaceAll("`", ""))) {
                    chargeAmNewInt = Integer.valueOf(String.valueOf(chargeAmNew));
                }
                System.out.println("---合并数据（单位：分）---\n---合并前金额：" + amtInt + "\n---退款金额：" + amtNewInt + "\n---合并后"
                        + String.valueOf(amtInt + amtNewInt) +"\n---合并后手续费："+String.valueOf(chargeAmInt + chargeAmNewInt));
                //统一在分转元set 手续费
                accountDetailsFile1.setService_charge(AmountUtil.convertCent2DollarShort(String.valueOf(chargeAmInt + chargeAmNewInt)));
                //统一在分转元set 消费金额
                accountDetailsFile1.setTotal_sum(AmountUtil.convertCent2DollarShort(String.valueOf(amtInt + amtNewInt)));
                //原先的订单set退款金额
                accountDetailsFile1.setRefund_amount(String.valueOf(amtNewInt));
                accountDetailsFile1.setPlatform_refund_number(accountDetailsFile.getPlatform_refund_number().replaceAll("`", ""));
                //记录合并标记
                accountDetailsFile1.setRemark2("合并订单:" + amtInt + "+(转入退款" + amtNewInt + ")");
            } else {
                //没有此订单号放入map
                map.put(accountDetailsFile.getPlatform_order_number().replaceAll("`", ""), accountDetailsFile);
            }
        }
        System.out.println("合并前共计:" + list.size() + "，合并后共计:" + map.size());

        //插入对账结果
        int resId = insertResult(accountDetails);
        //总相差
        int sumRes = 0;
        //手续费
        int serviceCharge = 0;

        //支付宝总对账单金额
        int zfbTotailAmount = 0;

        //进行比对
        for (int i = 0; i < accountDetailList.size(); i++) {
            //记账金额
            int sumAmt = accountDetailList.get(i).getSumAmt();
            //记账单合计总和
            accCount += sumAmt;
            //如果对账单中存在此交易流水
            if (ToolUtil.isNotEmpty(map.get(accountDetailList.get(i).getTransNo()))) {
                //对账单对象
                AccountDetailsFile accountDetailsFile3 =
                        (AccountDetailsFile) map.get(accountDetailList.get(i).getTransNo());
                String amtNewmap = AmountUtil.convertDollar2Cent(accountDetailsFile3.getTotal_sum().replaceAll("`",
                        ""));
                //对账单发生金额(分)  已+退款金额
                int mapAmt = (int) Integer.valueOf(amtNewmap);
                //相差金额
                int res = sumAmt - mapAmt;
                //相差总和
                sumRes += res;
                //金额不相等  记录
                if (res != 0) {
                    //记录总和
                    accCount += res;
                    accountDetailList.get(i).setRemark("记账金额:" + sumAmt + " 对账单金额:" + mapAmt + " 相差:" + res);
                    noacclist.add(accountDetailList.get(i));
                    inserAccountDetail(accountDetailList.get(i), resId);
                }
            } else {
                //对账单中找不到此比记账   （有记账但是没有发生交易）
                accountDetailList.get(i).setRemark("有记账但是没有发生交易");
                noacclist.add(accountDetailList.get(i));
                inserAccountDetail(accountDetailList.get(i), resId);
            }
        }

        //遍历对账单
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            boolean falg = false;
            for (AccountDetails details : accountDetailList) {
                if (entry.getKey().equals(details.getTransNo())) {
                    falg = true;
                }
            }

            AccountDetailsFile detailsFile = (AccountDetailsFile) entry.getValue();

            String chargeAmt = AmountUtil.convertDollar2Cent(detailsFile.getService_charge().replaceAll("`",
                    ""));
            //对账单手续费（支付宝，官方微信）
            int amt = (int) Integer.valueOf(chargeAmt);
            serviceCharge += amt;

            if ("zfb".equals(accountDetails.getAccType())){
                String zfbTotail = AmountUtil.convertDollar2Cent(detailsFile.getTotal_sum().replaceAll("`",
                        ""));
                zfbTotailAmount += Integer.valueOf(zfbTotail);
            }

            if (!falg) {
                String amtNewmap = AmountUtil.convertDollar2Cent(detailsFile.getTotal_sum().replaceAll("`", ""));
                //对账单发生金额(分)
                int mapAmt = (int) Integer.valueOf(amtNewmap);
                reconCunt += mapAmt;
                detailsFile.setRemark("无记账");
                nofilelist.add(detailsFile);
                //插入数据库
                inserAccountDetailsFile(detailsFile, resId);
            }
        }
        //更新结果表
        updateResult(noacclist.size(), nofilelist.size(), resId);
        Map<String, Object> hasmap = new HashMap<String, Object>();

        hasmap.put("reconCunt",reconCunt+"");
        hasmap.put("accCount",accCount+"");
        hasmap.put("sumRes",sumRes);
        hasmap.put("zfbTotailAmount",zfbTotailAmount);
        hasmap.put("nofileNo",nofilelist.size()+"");
        hasmap.put("noaccNo",noacclist.size()+"");
        hasmap.put("serviceCharge", serviceCharge);
        System.out.println("----记账单对不上订单：" + noacclist.size() + "\n----对账单对不上订单：" + nofilelist.size());
        return hasmap;

    }

    /**
     * 更新结果
     *
     * @param reconciliationCount
     * @param bookkeepingCount
     * @param id
     */
    private void updateResult(int reconciliationCount, int bookkeepingCount, int id) {
        ReconciliationResult result = new ReconciliationResult();
        result.setReconciliationCount(reconciliationCount);
        result.setBookkeepingCount(bookkeepingCount);
        result.setId(id);
        resultMapper.updateById(result);
    }


    /**
     * 从输入流中获取字节数组
     *
     * @param inputStream
     * @return
     * @throws IOException
     */
    public static byte[] readInputStream(InputStream inputStream) throws IOException {
        byte[] buffer = new byte[1024];
        int len = 0;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        while ((len = inputStream.read(buffer)) != -1) {
            bos.write(buffer, 0, len);
        }
        bos.close();
        return bos.toByteArray();
    }

    /**
     * 定时对账
     *
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> recondition(String billDate, MchConfig mchConfig) {

        Map<String, Object> rsmap = new HashMap<>();

        //总手续费
        int serviceCharge = 0;

        //总消费金额
        int trnamt = 0;

        //问题件记录金额
        int problemAmount = 0;

        StringBuffer problemDetail = new StringBuffer();

        AccountDetails accountDetails = new AccountDetails();
        String beginTime = cn.hutool.core.date.DateUtil.offsetDay(new DateTime(), -1).toString(DatePattern.NORM_DATE_PATTERN);
        String endTime = cn.hutool.core.date.DateUtil.offsetDay(new DateTime(), 0).toString(DatePattern.NORM_DATE_PATTERN);
        try {
            beginTime = DateUtil.strToDateFormat(billDate);
            accountDetails.setBeginTime(beginTime);
            accountDetails.setEndTime(endTime);
            accountDetails.setMchNo(mchConfig.getMchNo());
            accountDetails.setBillDate(DateUtil.strToDateFormat(billDate));
        } catch (ParseException e) {
            e.printStackTrace();
        }

        DownloadInfo downloadInfo = new DownloadInfo();
        downloadInfo.setBillDate(billDate);
        downloadInfo.setMchNo(mchConfig.getMchNo());
        List<DownloadInfo> list = downloadInfoMapper.selectDownloadInfos(downloadInfo);

        if (list != null && list.size() > 0) {
            for (DownloadInfo downloadInfo1 : list) {
                Mch mch = new Mch();
                mch.setMchNo(mchConfig.getMchNo());
                mch.setAccMchId(downloadInfo1.getChannel());
                Mch mch1 = mchMapper.selectMchByAccMchId(mch);
                try {
                    File directory = new File("E:\\log");
                    String projectPath = directory.getAbsolutePath();
                    File file = downloadFileToFTP(projectPath, downloadInfo1.getDownloadUrl());
                    Map<String, Object> rsmap2 = new HashMap<>();
                    String accType = mch1.getAccType();
                    if (file != null) {
                        accountDetails.setAccType(accType);
                        accountDetails.setAccMchId(mch1.getAccMchId());
                        List<AccountDetailsFile> accountDetailsFileList = getAccountDetailsFile(accType,file);
                        if (accountDetailsFileList != null && accountDetailsFileList.size() > 0){
                            rsmap2 = halder(accountDetails,accountDetailsFileList,mch1.getAccType());
                        }

                        if (rsmap2 != null && rsmap2.size() > 0){
                            //总手续费计算
                            Map<String,Object> map = getAllCharge(rsmap2,accType,mchConfig,mch1,billDate);
                            serviceCharge += (int)map.get("serviceCharge");
                            trnamt += (int)map.get("trnamt");
                            problemDetail.append(map.get("problemDetail"));
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            // 计算通道手续费，计入分润账户
            try {
                //记录问题键
                ProblemRecord problemRecord = new ProblemRecord();
                problemRecord.setBillDate(DateUtil.strToDateFormat(billDate));
                problemRecord.setMchNo(mchConfig.getMchNo());
                ProblemRecord problemRecord1 = problemRecordMapper.selectProblemRecord(problemRecord);
                if (problemRecord1!= null && problemRecord1.getProblemDetail() != null){
                    problemAmount = Integer.valueOf(problemRecord1.getProblemDetail());
                    int amount = serviceCharge + problemAmount;
                    if (serviceCharge > 0 && amount == 0){
                        //问题件所差金额刚好等于手续费，则把手续费计入分润账户
                        dispatch(mchConfig,"2",serviceCharge+"","0","");
                        System.out.println("问题件所差金额刚好等于手续费，则把手续费计入分润账户"+amount);
                        ProblemRecord problemRecord2 = new ProblemRecord();
                        //问题键状态 0：未解决 1：已解决
                        problemRecord2.setStatus("1");
                        problemRecord2.setId(problemRecord1.getId());
                        problemRecordMapper.updateByPrimaryKey(problemRecord2);
                    } else {
                        //如果问题件所加金额不等于手续费，则记录所差金额
                        System.out.println("如果问题件所加金额不等于手续费，则记录所差金额"+amount);
                        problemDetail.append("问题件记录金额："+problemAmount+"分，手续费总和: " + serviceCharge+"分，总消费金额"+trnamt+"分");
                        ProblemRecord problemRecord2 = new ProblemRecord();
                        problemRecord2.setRemark2(problemDetail.toString());
                        problemRecord2.setId(problemRecord1.getId());
                        problemRecordMapper.updateProblemRecord(problemRecord2);
                    }
                }
            } catch (ParseException e) {
                e.printStackTrace();
            }

            System.out.println(problemDetail.toString());
        }
        return rsmap;
    }

    /**
     * 获取总手续费
     * @param rsmap2
     * @param accType
     * @param mchConfig
     * @param mch
     * @param billDate
     * @return
     */
    private Map<String, Object> getAllCharge(Map<String, Object> rsmap2,String accType,MchConfig mchConfig,Mch mch,String billDate){

        Map<String,Object> map = new HashMap<>();

        int serviceCharge = (Integer) rsmap2.get("serviceCharge");

        String nofileNo = (String) rsmap2.get("nofileNo");

        String noaccNo = (String) rsmap2.get("noaccNo");

        int sumRes = (int) rsmap2.get("sumRes");

        int zfbTotailAmount = (int) rsmap2.get("zfbTotailAmount");

        String accCount = (String) rsmap2.get("accCount");

        String reconCunt = (String) rsmap2.get("reconCunt");

        int trnamt = 0;

        int wxCharge = 0;

        //如果是兴业微信，需要通过流水来查询出兴业微信总手续费
        if ("wx".equals(accType)) {
            TransactionDetails transactionDetails = new TransactionDetails();
            transactionDetails.setMchno(mchConfig.getMchNo());
            transactionDetails.setAccType(mch.getAccType());
            try {
                transactionDetails.setBillDate(DateUtil.strToDateFormat(billDate));
            } catch (ParseException e) {
                e.printStackTrace();
            }
            TransactionDetails transaction = transactionDetailsMapper.selectTransactionDetail(transactionDetails);
            if (transaction != null){
                String memo = transaction.getMemo().split("银联微信")[1];
                trnamt = Integer.valueOf(AmountUtil.convertDollar2Cent(transaction.getTrnamt()+""));
                wxCharge = Integer.valueOf(AmountUtil.convertDollar2Cent(AmountUtil.strToGetNumber(memo)));
            }
        } else if ("zfb".equals(accType)){
            trnamt += zfbTotailAmount;
        }

        //微信+支付宝手续费
        serviceCharge += wxCharge;


        StringBuffer problemDetail = new StringBuffer();
        problemDetail.append(mch.getAccType()+"产生手续费："+serviceCharge+"分\n");
        problemDetail.append(mch.getAccType()+"对账单总金额："+trnamt+"分\n");
        if (Integer.valueOf(nofileNo) > 0){
            problemDetail.append(mch.getAccType()+"对账单对不上订单 -->合计金额：" + reconCunt + "分，" + "合计记录：" + nofileNo + "条\n");
        }
        if (Integer.valueOf(noaccNo) > 0){
            problemDetail.append(mch.getAccType()+"记帐单对不上订单 -->合计金额：" + accCount + "分，" + "相差合计金额：" + sumRes + "分，" + "合计记录：" + noaccNo+"条\n");
        }
        map.put("trnamt",trnamt);
        map.put("serviceCharge",serviceCharge);
        map.put("problemDetail",problemDetail);
        return map;
    }

    /**
     *  根据不通支付通道类型整理数据 ---> 提取公共部分
     * @param accType
     * @param file
     * @return
     */
    private List<AccountDetailsFile> getAccountDetailsFile(String accType,File file){
        List<AccountDetailsFile> list = new ArrayList<>();
        //根据不通支付通道类型整理数据
        if ("zfb".equals(accType)) {
            //支付宝
            //导入的对账明细
            Map<String, String> columnMapping = CsvUtils.getZFBColumnMapping();
            List<AccountDetailsFileZfb> accountDetailsFileZfbList = CsvUtils.getCsvData(file, AccountDetailsFileZfb.class, columnMapping);
            if (accountDetailsFileZfbList != null && accountDetailsFileZfbList.size() > 0){
                for (AccountDetailsFileZfb accountDetailsFileZfb : accountDetailsFileZfbList){
                    double serviceCharge = - Double.valueOf(accountDetailsFileZfb.getServiceCharge());
                    String tradingStatus = accountDetailsFileZfb.getTradingStatus().replaceAll("\\t","");
                    String zfbTransNo = accountDetailsFileZfb.getZfbTransNo().replaceAll("\\t","");
                    String refundNo = accountDetailsFileZfb.getRefundNo().replaceAll("\\t","");
                    if ("退款".equals(tradingStatus)){
                        double refundAmt = - Double.valueOf(accountDetailsFileZfb.getOrderAmount());
                        AccountDetailsFile accountDetailsFile = new AccountDetailsFile(accountDetailsFileZfb.getCompletionTime(),zfbTransNo,"转入退款","0","0",refundNo,refundAmt+"","","",serviceCharge+"","0");
                        list.add(accountDetailsFile);
                    } else if ("交易".equals(tradingStatus)){
                        AccountDetailsFile accountDetailsFile = new AccountDetailsFile(accountDetailsFileZfb.getCompletionTime(),zfbTransNo,"支付成功",accountDetailsFileZfb.getOrderAmount(),"0",refundNo,"0","","",serviceCharge+"","0");
                        list.add(accountDetailsFile);
                    }

                }
            }
        } else if ("wx".equals(accType)) {
            //兴业微信 --> 威富通微信对账单
            //解析导入的对账明细
            Map<String, String> columnMapping = CsvUtils.getWXColumnMapping();
            List<AccountDetailsFileXywx> accountDetailsFileXywxList = CsvUtils.getCsvData(file, AccountDetailsFileXywx.class, columnMapping);
            if (accountDetailsFileXywxList != null && accountDetailsFileXywxList.size() > 0){
                for (AccountDetailsFileXywx accountDetailsFileXywx : accountDetailsFileXywxList){
                    int serviceCharge = Integer.valueOf("0");
                    AccountDetailsFile accountDetailsFile = new AccountDetailsFile(accountDetailsFileXywx.getTransaction_time(),accountDetailsFileXywx.getPlatform_order_number(),accountDetailsFileXywx.getTrading_status(),accountDetailsFileXywx.getTotal_sum(),accountDetailsFileXywx.getBusiness_envelopes(),accountDetailsFileXywx.getPlatform_refund_number(),accountDetailsFileXywx.getRefund_amount(),accountDetailsFileXywx.getRefund_type(),accountDetailsFileXywx.getRefund_status(),serviceCharge+"","0");
                    list.add(accountDetailsFile);
                }
            }
        } else if ("wxgf".equals(accType)) {
            //官方微信
            //解析导入的对账明细
            Map<String, String> columnMapping = CsvUtils.getWXGFColumnMapping();
            List<AccountDetailsFileWxgf> accountDetailsFileXywxList = CsvUtils.getCsvData(file, AccountDetailsFileWxgf.class, columnMapping);

            if (accountDetailsFileXywxList != null && accountDetailsFileXywxList.size() > 0){
                for (AccountDetailsFileWxgf accountDetailsFileWxgf : accountDetailsFileXywxList){
                    AccountDetailsFile accountDetailsFile = new AccountDetailsFile(accountDetailsFileWxgf.getTransaction_time(),accountDetailsFileWxgf.getPlatform_order_number(),accountDetailsFileWxgf.getTrading_status(),accountDetailsFileWxgf.getTotal_sum(),"0",accountDetailsFileWxgf.getPlatform_refund_number(),accountDetailsFileWxgf.getRefund_amount(),accountDetailsFileWxgf.getRefund_type(),accountDetailsFileWxgf.getRefund_status(),accountDetailsFileWxgf.getService_charge(),accountDetailsFileWxgf.getRate());
                    list.add(accountDetailsFile);
                }
            }
        } else {
            //其他通道

        }
        return list;
    }

    /**
     * 插入记账单对不上的数据
     *
     * @param accountDetails
     */
    private void inserAccountDetail(AccountDetails accountDetails, int resultId) {
        ReconciliationResultInfo resultInfo = new ReconciliationResultInfo();
        resultInfo.setResultId(resultId);
        resultInfo.setCreateTime(new Date());
        //这里注意set是group by后的总和
        resultInfo.setEventAmt(accountDetails.getSumAmt());
        resultInfo.setEventTime(accountDetails.getEventTime());
        resultInfo.setMchNo(accountDetails.getMchNo());
        resultInfo.setOrderNo(accountDetails.getOrderNo());
        resultInfo.setTransNo(accountDetails.getTransNo());
        resultInfo.setRemark(accountDetails.getRemark());
        resultInfo.setType("0");
        resultInfoMapper.insert(resultInfo);
    }

    /**
     * 插入对账单对不上的数据
     *
     * @param accountDetailsFile
     */
    private void inserAccountDetailsFile(AccountDetailsFile accountDetailsFile, int resultId) {
        ReconciliationResultInfo resultInfo = new ReconciliationResultInfo();
        resultInfo.setOrderNo(accountDetailsFile.getPlatform_order_number().replaceAll("`", ""));
        String amtOld = AmountUtil.convertDollar2Cent(accountDetailsFile.getTotal_sum().replaceAll("`", ""));
        int amtInt = (int) Integer.valueOf(String.valueOf(amtOld));
        resultInfo.setResultId(resultId);
        resultInfo.setEventAmt(amtInt);
        resultInfo.setEventTime(cn.hutool.core.date.DateUtil.parse(accountDetailsFile.getTransaction_time().replaceAll("`", "")));
        resultInfo.setRemark(accountDetailsFile.getRemark());
        resultInfo.setType("1");
        resultInfo.setRemark1(accountDetailsFile.getRemark2());
        resultInfo.setPlatformRefundNumber(accountDetailsFile.getPlatform_refund_number());
        resultInfo.setRefundAmount(accountDetailsFile.getRefund_amount());
        resultInfoMapper.insert(resultInfo);
    }

    /**
     * 插入对账结果 返回id
     *
     * @param accountDetails
     * @return
     */
    private int insertResult(AccountDetails accountDetails) {
        ReconciliationResult result = new ReconciliationResult();
        result.setRdChannel(accountDetails.getRdChannel());
        result.setMchNo(accountDetails.getMchNo());
        result.setIsHisData(accountDetails.getIsHisData());
        result.setCreateTime(new Date());
        result.setBeginTime(accountDetails.getBeginTime());
        result.setEndTime(accountDetails.getEndTime());
        resultMapper.insert(result);
        return result.getId();
    }

    /**
     * 核对余额分账
     *
     * @param passportXyReportDetail
     * @return
     */
    @Override
    public Map<String, String> dispatch(PassportXyReportDetail passportXyReportDetail) {
        Map<String, String> resultMap = new HashMap<>();
        String url = "http://127.0.0.1:3023/api/deposit.htm";
        //组装要发送分账的参数
        HashMap<String, String> params = getDisPatchParams(passportXyReportDetail);
        //发送分账请求
        String json = null;
        json = ToolUtil.txn(url, params, "");//发送请求
        //解析返回结果
        JSONObject po = JSONObject.parseObject(json);//jsonString 转 jsonObject

        String resultData = po.getString("data");//返回data
        if (ToolUtil.isNotEmpty(resultData)) {
            resultMap.put("code", JSONObject.parseObject(resultData).getString("code"));
            resultMap.put("code", JSONObject.parseObject(resultData).getString("code"));
        }
        return resultMap;
    }

    private HashMap<String, String> getDisPatchParams(PassportXyReportDetail passportXyReportDetail) {

        List list = new ArrayList();
        String token = "9b389216c3c55a7c535510b33b9e6ea5";
        String url = "http://127.0.0.1:3023/api/dispatchMchAccntRecon.htm";
        String orderNo = passportXyReportDetail.getOutTransNo();
        Map<String, Object> bizContentMap = new HashMap<>();
        bizContentMap.put("mch_accnt_no", passportXyReportDetail.getMchSubNo());
        bizContentMap.put("order_no", orderNo);
        bizContentMap.put("trans_no", passportXyReportDetail.getTranNo());
        if ("0".equals(passportXyReportDetail.getDetailIncome())) {
            bizContentMap.put("dispatch_event", "refund");
        } else {
            bizContentMap.put("dispatch_event", "pay");
        }
        bizContentMap.put("dispatch_type", 1);//业务数据
        BigDecimal bignum2 = new BigDecimal("100");
        if (passportXyReportDetail.getDetailIncome().compareTo(BigDecimal.ZERO) > 0) {
            bizContentMap.put("amount", passportXyReportDetail.getDetailIncome().multiply(bignum2).longValue());
        } else if (passportXyReportDetail.getDetailPayout().compareTo(BigDecimal.ZERO) > 0) {
            bizContentMap.put("amount", "-" + passportXyReportDetail.getDetailPayout().multiply(bignum2).longValue());
        }

        list.add(bizContentMap);
        Map<String, Object> accntDetaiMap = new HashMap<>();
        accntDetaiMap.put("split_accnt_detail", list);
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("biz_type", "mchaccnt.dispatch");
        dataMap.put("mch_no", "MH20181120110623Tji0");
        dataMap.put("sign_type", "MD5");
        dataMap.put("timestamp", DateUtil.getCurrentDate());
        dataMap.put("biz_content", accntDetaiMap);
        dataMap.put("out_trans_no", orderNo);//公共参数

        String data = JSON.toJSONString(dataMap);
        HashMap<String, String> params = new HashMap<String, String>();
        String sign = PayDigestUtil.md5(data + "&" + dataMap.get("timestamp") + token, "UTF-8");//计算签名
        params.put("data", data);
        params.put("sign", sign);//组装请求参数
        return params;
    }
}


